/**
 * @file    BinInput.h
 * @brief   Binary array data input
 * @date    2014/02
 * @version
 * Copyright (c)
 */

#ifndef BinInput_h__
#define BinInput_h__

#include <fstream>
#include <list>
#include <string>
#include <vector>

#include "TypeCast.h"

struct transfer
{
    transfer()
    {
        from = to = 0;
        mult = 1.0;
    };
    transfer(const UGINT64 _from, const UGINT64 _to, const double _mult)
    {
        from = _from;
        to = _to;
        mult = _mult;
    };

    UGINT64 from, to;
    double mult;
};

class PropArray
{
public:
    PropArray();
    PropArray(const PropArray &src);
    ~PropArray();
    void operator=(const PropArray &src);

    double operator[](const UGINT64 i) const
    {
        if (type == 'd')
            return fdata[i];
        else
            return (double)idata[i];
    };

    void Clear();
    bool is_empty() const { return length == 0; }

    bool ReMap(const UGINT64 Net_size, const UGINT64 Pre_Net_size, const std::vector<transfer> &Moves);

    char type; // 'i' or 'd' (int or double)
    UGINT64 length;
    int *idata;
    double *fdata;

    bool on_grid;

    char time_type; // 'd' or 't' (date or time)
    double time;

    double Max, Min, Avg;

    std::string name;
    std::string unit;
};

struct Name_Loc 
{
    /* 数据类型，“d”为“double”类型，“i”为“int”类型 */
    char type = 'u'; 

    /* 数组长度，即有效网格数量 */
    UGINT64 length = 0; 

    /**
     * 数据是否与网格对应。
     * 对于油藏数据，“true”表示数据可以对应到网格，“false”表示数据不可以对应到网格（不能用于画图）。
     */
    bool on_grid = false; 

    /* 时间类型。“d”表示日期，“t”表示时间 */
    char time_type; 

    /**
     * 数据对应的时间或日期，当time_type等于“t”时，此数据就是天数，第一个时间步是第0天；
     * 当time_type等于“d”时，此数据为YYYYMMDD.x，小数部分为0.x天，
     * 例如20240801.01，表示2024年8月1日的第0.24小时
     */
    double time; 

    /**
     * Max 数据数组中的最大值
     * Min 数据数组中的最小值
     * Avg 数据数组中的平均值
     */
    double Max, Min, Avg;

    /**
     * 数据属性的名称，如“POIL”
     * 数据的单位，如“bar”
     */
    std::string name;
    std::string unit;

    UGINT64 fcount = 0;
    UGINT64 offset = 0;
};

class BinReader
{
public:
    BinReader() {};
    BinReader(const std::string &_casename);
    ~BinReader() {};

    // Before calling accessing subroutines, scan binary files first
    void Scan(const std::string &_casename);

    // return mismatch
    int ReMap(const double *GridInfo_in, const int *SubregInfo_in, const int *Host_map_in, const int *Gross2Net_in, const int *Net2Gross_in);

    void Clear();

    UGINT64 NStep() const { return dym_props.size(); };

    // return if find
    bool GetDynamicData(const UGINT64 tstep, const std::string &name, PropArray &a, const bool remap = false) const;
    bool GetStaticData(const std::string &name, PropArray &a, const bool remap = false) const;

    UGINT64 NetSize() const { return Net2Gross.size(); };   // = GridInfo[8]
    UGINT64 GrossSize() const { return Gross2Net.size(); }; // = GridInfo[2]

    UGINT64 GridType() const { return GridInfo[0]; }; // 0-Cartesian, 1-Stage, 2-CPG, 3-PEBI
    UGINT64 PoroLvl() const { return GridInfo[1]; };

    void GetAxisOffset(double &x0, double &y0, double &d0) const
    {
        x0 = AxisOffset[0];
        y0 = AxisOffset[1];
        d0 = AxisOffset[2];
    }

    // IJk grid info
    UGINT64 SGridNI() const { return GridInfo[3]; };
    UGINT64 SGridNJ() const { return GridInfo[4]; };
    UGINT64 SGridNK() const { return GridInfo[5]; };

    // IJk grid info: base gird & fine grid
    UGINT64 NumBaseGrid() const { return GridInfo[2] - GridInfo[6] - GridInfo[7]; };
    UGINT64 NumActBaseGrid() const { return GridInfo[9] + GridInfo[11] * (GridInfo[1] - 1); };
    UGINT64 NumFLvActBaseGrid() const { return GridInfo[9]; };
    UGINT64 NumMtxActBaseGrid() const { return GridInfo[11]; };

    UGINT64 NumFineGrid() const { return GridInfo[6]; };
    UGINT64 NumActFineGrid() const { return GridInfo[10] + GridInfo[12] * (GridInfo[1] - 1); };
    UGINT64 NumFLvActFineGrid() const { return GridInfo[10]; };
    UGINT64 NumMtxActFineGrid() const { return GridInfo[12]; };

    // Embedded fracture
    UGINT64 NumEmdfFace() const { return GridInfo[7]; };
    UGINT64 NumActEmdfFace() const { return GridInfo[13]; };

    // PEBI grid info
    UGINT64 PGridNG() const { return GridInfo[14]; };
    UGINT64 PGridNLy() const { return GridInfo[15]; };
    UGINT64 PGridNV() const { return GridInfo[18]; };

    UGINT64 UnitType() const { return GridInfo[19]; };

    // gross to net grid mapping and net to gross grid mapping
    std::vector<int> Gross2Net;
    std::vector<int> Net2Gross;

    UGINT64 GridInfo[32] = {0};
    double AxisOffset[3] = {0};

    std::vector<std::vector<Name_Loc> > dym_props;
    std::vector<Name_Loc> stat_props;
    std::string casename;

private:
    UGINT64 origin_net_size = 0;
    UGINT64 origin_GridInfo[32] = {0};
    std::vector<int> origin_Gross2Net;
    std::vector<transfer> Moves;
};

#endif // BinInput_h__
